import { _decorator, Component, Asset, Label } from 'cc';
import * as cc from 'cc';
const { ccclass, property } = _decorator;

/**
 * 热更新 逻辑
 * @description 交流群：415468592
 */
@ccclass('HotUpdate')
export default class HotUpdate extends Component {
    @property({ type: Asset, displayName: "热更资源" })
    private manifestUrl: Asset | null = null;
    @property({ type: Label, displayName: "版本号" })
    private lbl_version: Label | null = null;
    private _updating: boolean = false;
    private _canRetry: boolean = false;
    private _storagePath: string = '';
    private _am: any = null;
    private _failCount: number = 0;

    onLoad() {
        if (!cc.sys.isNative) return;
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'qygame-remote-asset');

        let versionCompareHandle = function (versionA, versionB) {
            let vA = versionA.split('.');
            let vB = versionB.split('.');
            console.log("版本号对比：" + vA + "------" + vB)
            for (let i = 0; i < vA.length; ++i) {
                let a = parseInt(vA[i]);
                let b = parseInt(vB[i] || 0);
                if (a === b) {
                    continue;
                } else {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            } else {
                return 0;
            }
        };

        this._am = new jsb.AssetsManager(this.manifestUrl.nativeUrl, this._storagePath, versionCompareHandle);
        this._am.setVerifyCallback((filePath, asset) => {
            return true;
        });

        if (this.lbl_version) {
            this.lbl_version.string = `src:${this._am.getLocalManifest().getVersion()}`;
        }
    }

    start() {
        if (!cc.sys.isNative) return;
        this.checkUpdate();
    }
    onDestroy() {
        if (cc.sys.isNative && this._am) {
            this._am.setEventCallback(null);
            this._am = null;
            this.showLog("清空 _am")
        }
    }
    private showLog(msg: string) {
        cc.log('[HotUpdateModule][showLog]----' + msg);
    }
    private retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;
            this._am.downloadFailedAssets();
        }
    }
    private updateCallback(event) {
        let needRestart = false;
        let failed = false;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                this.showLog("没有发现本地manifest文件，跳过了热更新.");
                failed = true;
                break;
            // //更新进度
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                let msg = event.getMessage();
                this.disPatchRateEvent(percent);
                this.showLog("updateCallback更新进度：" + percent + ', msg: ' + msg);
                break;

            // //下载manifest文件失败，跳过热更新
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                this.showLog("下载manifest文件失败，跳过热更新.");
                failed = true;
                break;

            // //已是最新版本
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                this.showLog("已是最新版本.");
                failed = true;
                break;
            // //更新结束
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                this.showLog("更新结束." + event.getMessage());
                this.disPatchRateEvent(1);
                needRestart = true;
                break;
            // //更新错误
            case jsb.EventAssetsManager.UPDATE_FAILED:
                this.showLog("更新错误." + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this._failCount++;
                this.retry();
                break;
            // //更新过程中错误
            case jsb.EventAssetsManager.ERROR_UPDATING:
                this.showLog('更新过程中错误: ' + event.getAssetId() + ', ' + event.getMessage());
                break;
            // //解压错误
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                this.showLog('解压错误');
                break;
            default:
                break;
        }

        if (failed) {
            this._am.setEventCallback(null);
            this._updating = false;
        }

        if (needRestart) {
            this._am.setEventCallback(null);
            let searchPaths = jsb.fileUtils.getSearchPaths();
            let newPaths = this._am.getLocalManifest().getSearchPaths();
            Array.prototype.unshift.apply(searchPaths, newPaths);
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);
            setTimeout(() => {
                cc.game.restart();
            }, 100);
        }
    }
    private hotUpdate() {
        if (this._am && !this._updating) {
            this._am.setEventCallback(this.updateCallback.bind(this));
            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                let url = this.manifestUrl.nativeUrl;
                this._am.loadLocalManifest(url);
            }
            this._failCount = 0;
            this._am.update();
            this._updating = true;
        }
    }
    private checkCallback(event) {
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                this.showLog("没有发现本地manifest文件，跳过了热更新.");
                this.hotUpdateFinish(true);
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                this.showLog("下载manifest文件失败，跳过热更新.");
                this.hotUpdateFinish(false);
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                this.showLog("已更新.");
                this.hotUpdateFinish(true);
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND: {
                // 有新版本
                this.showLog("有新版本,需要更新");
                this._updating = false;
                this.hotUpdate();
                return;
            }
            case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
                // 有新版本
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                let msg = event.getMessage();
                this.showLog("checkCallback更新进度：" + percent + ', msg: ' + msg);
                return;
            }
            default:
                console.log('event.getEventCode():' + event.getEventCode());
                return;
        }
        this._am.setEventCallback(null);
        this._updating = false;
    }
    private checkUpdate() {
        if (this._updating) {
            cc.log("检测更新中...");
            return;
        }
        if (!this._am) {
            console.log("热更管理器已被释放...");
            return;
        }
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            let url = this.manifestUrl.nativeUrl;
            //@ts-ignore
            if (cc.assetManager.md5Pipe) {
                //@ts-ignore
                url = cc.assetManager.md5Pipe.transformURL(url);
            }
            this._am.loadLocalManifest(url);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            this.showLog('加载manifest文件失败');
            return;
        }
        this._am.setEventCallback(this.checkCallback.bind(this));
        this.showLog('检查更新...');
        this._am.checkUpdate();
        this._updating = true;
        this.disPatchRateEvent(0.01);
    }
    private hotUpdateFinish(result) {
        cc.director.emit('HotUpdateFinish', result);
    }
    private disPatchRateEvent(percent) {
        if (percent > 1) {
            percent = 1;
        }
        cc.director.emit('HotUpdateRate', percent);
    }
}